package sample3.provider.dml;

import java.util.ArrayList;
import java.util.List;

/**
 * <pre>
 * ...
 * SqliteWhereBuilder where = new SqliteWhereBuilder();
 * where.addAndEquals("_id", 10);
 * 
 * SqliteUpdateBuilder sql = new SqliteUpdateBuilder(uri);
 * sql.addContentValue("key", 0);
 * sql.addAndWhere(where);
 * int update = sql.update(getContentResolver());
 * ...
 * </pre>
 */
public class SqliteWhereBuilder {
	private StringBuilder mWhere = new StringBuilder();
	private List<String> mArgs = new ArrayList<String>();
	private List<SqliteWhereBuilder> mAndOtherWheres = new ArrayList<SqliteWhereBuilder>();
	private List<SqliteWhereBuilder> mOrOtherWheres = new ArrayList<SqliteWhereBuilder>();

	public SqliteWhereBuilder() {
	}

	private void whereAnd(boolean and) {
		if (and) {
			if (mWhere.length() != 0) {
				mWhere.append(" AND ");
			}
		} else {
			if (mWhere.length() != 0) {
				mWhere.append(" OR ");
			}
		}
	}

	private static String[] stringArray(Number... args) {
		String[] strs = new String[args.length];
		for (int i = 0; i < strs.length; i++) {
			strs[i] = args[i].toString();
		}
		return strs;
	}

	private SqliteWhereBuilder whereEquals(boolean and, String column, String arg) {
		whereAnd(and);
		mWhere.append(column).append("=?");
		mArgs.add(arg);
		return this;
	}

	// ? column != ?
	private SqliteWhereBuilder whereNotEquals(boolean and, String column, String arg) {
		whereAnd(and);
		mWhere.append(column).append("!=?");
		mArgs.add(arg);
		return this;
	}

	// ? column IN(?,?,?...)
	private SqliteWhereBuilder whereIn(boolean and, String column, String... args) {
		whereAnd(and);
		StringBuilder inArgs = new StringBuilder();
		for (String arg : args) {
			if (inArgs.length() != 0) {
				inArgs.append(",");
			}
			inArgs.append(arg);
			mArgs.add(arg);
		}
		mWhere.append(column).append(" IN(").append(inArgs).append(")");
		return this;
	}

	// ? column NOT IN(?,?,?...)
	private SqliteWhereBuilder whereNotIn(boolean and, String column, String... args) {
		whereAnd(and);
		StringBuilder inArgs = new StringBuilder();
		for (String arg : args) {
			if (inArgs.length() != 0) {
				inArgs.append(",");
			}
			inArgs.append(arg);
			mArgs.add(arg);
		}
		mWhere.append(column).append(" NOT IN(").append(inArgs).append(")");
		return this;
	}

	// ? column LIKE ?
	private SqliteWhereBuilder whereLike(boolean and, String column, String arg) {
		whereAnd(and);
		mWhere.append(column).append(" LIKE '").append(arg).append("')");
		return this;
	}

	// ? column NOT LIKE ?
	private SqliteWhereBuilder whereNotLike(boolean and, String column, String arg) {
		whereAnd(and);
		mWhere.append(column).append(" NOT LIKE '").append(arg).append("')");
		return this;
	}

	// ? column IS NULL
	private SqliteWhereBuilder whereIsNull(boolean and, String column) {
		whereAnd(and);
		mWhere.append(column).append(" IS NULL ");
		return this;
	}

	// ? column IS NOT NULL
	private SqliteWhereBuilder whereIsNotNull(boolean and, String column) {
		whereAnd(and);
		mWhere.append(column).append(" IS NOT NULL ");
		return this;
	}

	/**
	 * And otherWhere And otherWhere And otherWhere ...
	 * 
	 * @param otherWheres
	 * @return
	 */
	public SqliteWhereBuilder whereAndWhere(SqliteWhereBuilder... otherWheres) {
		for (SqliteWhereBuilder where : otherWheres) {
			mAndOtherWheres.add(where);
		}
		return this;
	}

	/**
	 * OR otherWhere OR otherWhere OR otherWhere ...
	 * 
	 * @param otherWheres
	 * @return
	 */
	public SqliteWhereBuilder whereOrWhere(SqliteWhereBuilder... otherWheres) {
		for (SqliteWhereBuilder where : otherWheres) {
			mOrOtherWheres.add(where);
		}
		return this;
	}

	/**
	 * AND column = ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndEquals(String column, String arg) {
		return whereEquals(true, column, arg);
	}

	/**
	 * AND column != ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndNotEquals(String column, String arg) {
		return whereNotEquals(true, column, arg);
	}

	/**
	 * AND column IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereAndIn(String column, String... args) {
		return whereIn(true, column, args);
	}

	/**
	 * AND column NOT IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereAndNotIn(String column, String... args) {
		return whereNotIn(true, column, args);
	}

	/**
	 * AND column LIKE ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndLike(String column, String arg) {
		return whereLike(true, column, arg);
	}

	/**
	 * AND column NOT LIKE ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndNotLike(String column, String arg) {
		return whereNotLike(true, column, arg);
	}

	/**
	 * AND column IS NULL
	 * 
	 * @param column
	 * @return
	 */
	public SqliteWhereBuilder whereAndIsNull(String column) {
		return whereIsNull(true, column);
	}

	/**
	 * AND column IS NOT NULL
	 * 
	 * @param column
	 * @return
	 */
	public SqliteWhereBuilder whereAndIsNotNull(String column) {
		return whereIsNotNull(true, column);
	}

	/**
	 * OR column = ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrEquals(String column, String arg) {
		return whereEquals(false, column, arg);
	}

	/**
	 * OR column != ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrNotEquals(String column, String arg) {
		return whereNotEquals(false, column, arg);
	}

	/**
	 * OR column IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereOrIn(String column, String... args) {
		return whereIn(false, column, args);
	}

	/**
	 * OR column NOT IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereOrNotIn(String column, String... args) {
		return whereNotIn(false, column, args);
	}

	/**
	 * OR column LIKE ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrLike(String column, String arg) {
		return whereLike(false, column, arg);
	}

	/**
	 * OR column NOT LIKE ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrNotLike(String column, String arg) {
		return whereNotLike(false, column, arg);
	}

	/**
	 * OR column IS NULL
	 * 
	 * @param column
	 * @return
	 */
	public SqliteWhereBuilder whereOrIsNull(String column) {
		return whereIsNull(false, column);
	}

	/**
	 * OR column IS NOT NULL
	 * 
	 * @param column
	 * @return
	 */
	public SqliteWhereBuilder whereOrIsNotNull(String column) {
		return whereIsNotNull(false, column);
	}

	/**
	 * AND column = ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndEquals(String column, Number arg) {
		return whereAndEquals(column, arg.toString());
	}

	/**
	 * AND column <> ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndNotEquals(String column, Number arg) {
		whereAnd(true);
		mWhere.append(column).append("<>?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * AND column IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereAndIn(String column, Number... args) {
		return whereAndIn(column, stringArray(args));
	}

	/**
	 * AND column NOT IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereAndNotIn(String column, Number... args) {
		return whereAndNotIn(column, stringArray(args));
	}

	/**
	 * AND column < ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndLessThan(String column, Number arg) {
		whereAnd(true);
		mWhere.append(column).append("<?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * AND column > ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndGreaterThan(String column, Number arg) {
		whereAnd(true);
		mWhere.append(column).append(">?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * AND column <= ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndLessThanOrEquals(String column, Number arg) {
		whereAnd(true);
		mWhere.append(column).append("<=?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * AND column >= ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereAndGreaterThanOrEquals(String column, Number arg) {
		whereAnd(true);
		mWhere.append(column).append(">=?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * AND lessThan <= column AND column <= greaterThan
	 * 
	 * @param column
	 * @param lessThan
	 * @param greaterThan
	 * @return
	 */
	public SqliteWhereBuilder whereAndBetween(String column, Number lessThan, Number greaterThan) {
		whereAndLessThanOrEquals(column, greaterThan);
		whereAndGreaterThanOrEquals(column, lessThan);
		return this;
	}

	/**
	 * AND column < lessThan AND greaterThan < column
	 * 
	 * @param column
	 * @param lessThan
	 * @param greaterThan
	 * @return
	 */
	public SqliteWhereBuilder whereAndNotBetween(String column, Number lessThan, Number greaterThan) {
		whereAndLessThan(column, lessThan);
		whereAndGreaterThan(column, greaterThan);
		return this;
	}

	/**
	 * OR column = ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrEquals(String column, Number arg) {
		return whereOrEquals(column, arg.toString());
	}

	/**
	 * OR column <> ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrNotEquals(String column, Number arg) {
		whereAnd(false);
		mWhere.append(column).append("<>?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * OR column IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereOrIn(String column, Number... args) {
		return whereOrIn(column, stringArray(args));
	}

	/**
	 * OR column NOT IN(?,?,?...)
	 * 
	 * @param column
	 * @param args
	 * @return
	 */
	public SqliteWhereBuilder whereOrNotIn(String column, Number... args) {
		return whereOrNotIn(column, stringArray(args));
	}

	/**
	 * OR column < ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrLessThan(String column, Number arg) {
		whereAnd(false);
		mWhere.append(column).append("<?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * OR column > ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrGreaterThan(String column, Number arg) {
		whereAnd(false);
		mWhere.append(column).append(">?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * OR column <= ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrLessThanOrEquals(String column, Number arg) {
		whereAnd(false);
		mWhere.append(column).append("<=?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * OR column >= ?
	 * 
	 * @param column
	 * @param arg
	 * @return
	 */
	public SqliteWhereBuilder whereOrGreaterThanOrEquals(String column, Number arg) {
		whereAnd(false);
		mWhere.append(column).append(">=?");
		mArgs.add(arg.toString());
		return this;
	}

	/**
	 * OR lessThan <= column AND column <= greaterThan
	 * 
	 * @param column
	 * @param lessThan
	 * @param greaterThan
	 * @return
	 */
	public SqliteWhereBuilder whereOrBetween(String column, Number lessThan, Number greaterThan) {
		whereOrLessThanOrEquals(column, greaterThan);
		whereOrGreaterThanOrEquals(column, lessThan);
		return this;
	}

	/**
	 * OR column < lessThan AND greaterThan < column
	 * 
	 * @param column
	 * @param lessThan
	 * @param greaterThan
	 * @return
	 */
	public SqliteWhereBuilder whereOrNotBetween(String column, Number lessThan, Number greaterThan) {
		whereOrLessThan(column, lessThan);
		whereOrGreaterThan(column, greaterThan);
		return this;
	}

	public String getWhere() {
		StringBuilder where = new StringBuilder();
		if (0 < mWhere.length()) {
			where.append(mWhere.toString());
		}
		//
		for (int i = 0; i < mAndOtherWheres.size(); i++) {
			if (0 == i && 0 < where.length()) {
				where.append(" AND ");
			}
			where.append(mAndOtherWheres.get(i).getWhere());
		}
		//
		for (int i = 0; i < mAndOtherWheres.size(); i++) {
			if (0 == i && 0 < where.length()) {
				where.append(" OR ");
			}
			where.append(mOrOtherWheres.get(i).getWhere());
		}

		return where.toString();
	}

	public String[] getWhereArgs() {
		for (SqliteWhereBuilder otherWhere : mAndOtherWheres) {
			for (String otherWhereArg : otherWhere.getWhereArgs()) {
				mArgs.add(otherWhereArg);
			}
		}
		for (SqliteWhereBuilder otherWhere : mOrOtherWheres) {
			for (String otherWhereArg : otherWhere.getWhereArgs()) {
				mArgs.add(otherWhereArg);
			}
		}
		return mArgs.toArray(new String[] {});
	}
}
